package com.ht.module.sys.service.impl;

import cn.hutool.core.util.StrUtil;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ht.abnormal.HtException;
import com.ht.abnormal.ValidateCodeException;
import com.ht.constant.BusConstant;
import com.ht.constant.DicConstants;
import com.ht.constant.RedisConstants;
import com.ht.module.sys.dto.LoginDto;
import com.ht.module.sys.entity.SysMenu;
import com.ht.module.sys.entity.SysUser;
import com.ht.module.sys.service.ISysUserService;
import com.ht.module.sys.service.LoginService;
import com.ht.module.sys.vo.LoginVo;
import com.ht.util.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author hejialun
 * @ClassName LoginServiceImpl
 * @description: TODO（登录ServiceImpl）
 * @date 2023-08-29-10:56
 * @version: 1.0
 */
@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private ISysUserService sysUserService;
    //过期时间
    @Value("${jwt.expireTime}")
    public long EXPIRE_TIME;
    @Resource
    private BCryptPasswordEncoder passwordEncoder;


    @Override
    public LoginVo login(LoginDto en) {
        //验证码校验
        String code = (String) redisUtil.get(RedisConstants.VERIFICATION_CODE_PREFIX + en.getRandomStr());
        if (StrUtil.isEmpty(code)) {
            throw new ValidateCodeException(BusConstant.VERIFICATION_CODE_PAST);
        }
        code = code.equals("0.0") ? "0" : code;
        if (!StrUtil.equals(code, en.getCode())) {
            throw new ValidateCodeException(BusConstant.VERIFICATION_CODE_NOT_CORRECT);
        }
        //查询出当前用户
        SysUser sysUser = sysUserService.getUserInfoByUsernameAll(en.getUsername());
        if (sysUser == null) {      //用户不存在，认证失败
            throw new UsernameNotFoundException("用户名不存在");
        }
        ////用户已被禁用，认证失败
        if (!StrUtil.equals(sysUser.getState(), DicConstants.State.ENABLE)) {
            throw new HtException("用户已被禁用！请联系管理员");
        }
        if (!passwordEncoder.matches(en.getPassword(), sysUser.getPassword())) {
            throw new UsernameNotFoundException("密码不正确！");
        }

        //清理数据权限
        redisUtil.del(RedisConstants.DATA_AUTHORITY+sysUser.getId());
        return generateToken(sysUser);
    }

    @Override
    public LoginVo generateToken(String userNumber) {
        SysUser sysUser = sysUserService.getUserInfoByUsernameAll(userNumber);
        return this.generateToken(sysUser);
    }

    @Override
    public LoginVo generateToken(SysUser sysUser) {
        //获取用户token
        String jwtToken =  JwtUtils.getUserToken(sysUser);
        //将当前用户的权限缓存下来
        Map<String, String> roleMap = new HashMap<>();
        //设置角色权限
        roleMap.put(RedisConstants.SYS_ROLE_CODES, String.join(",", sysUser.getRoles()));
        //设置菜单
        roleMap.put(RedisConstants.SYS_MENU_CODES, sysUser.getSysMenusNoTree().stream().map(SysMenu::getCode).collect(Collectors.joining(",")));
        //将用户信息存到redis
        redisUtil.set(RedisConstants.USER_INFOS_PREFIX + jwtToken, JwtUtils.getUserJwtMap(sysUser), EXPIRE_TIME);
        //将用户权限信息存到redis
        redisUtil.set(RedisConstants.USER_ROLE + sysUser.getUsername(), roleMap, EXPIRE_TIME);
        //吧用户信息返回给前端
        LoginVo vo = new LoginVo()
                .setUserInfo(sysUser)
                .setToken(jwtToken);
        return vo;
    }

    @Override
    public void lougout(HttpServletRequest request) {
        //清空token
        String token= UserUtil.getTokenByRequest(request);
        //删除token、权限
        redisUtil.del(RedisConstants.USER_INFOS_PREFIX+token);
        redisUtil.del(RedisConstants.USER_ROLE+ JwtUtils.getKey(token,"username"));
    }

}
